什么样的代码才是好的——李仙鹏
我所理解的好代码
代码规范——可读性
- 统一命名方式(Google Java代码风格)
- 尽量不要出现magic number
- 清晰的代码注释文档(改善Java文档的理由、建议和技巧)
- 控制方法或者函数的细粒度
- 减少代码冗余
代码耦合低——可扩展性、可移植性
- 尽量采用接口实现,减少继承
- 通用功能尽量抽取作为一个独立的方法,避免重复造轮子
- 设计模式并不能提高代码执行效率,但容易对代码进行模块切分,从而进行代码的解耦合。设计模式也可以算是程序员的一种通用“语言“,方便程序员之间的沟通.
- 根据不同OS或者语言特性,从大的架构上遵循MVC或者MVP或者其它类似的层次分明的设计模式。
严格的code review
- 能够发现潜在的bug、不合理的实现和是否遵从代码规范
- 有助于代码提交人员对自身代码质量的要求——面子问题
性能
以上这些点都是为了程序的健壮性、可扩展性、可移植性。最终目标,在核心开发人员变动或者产品需求变动后,都不会对代码维护、版本迭代和程序稳定性造成重大影响。
51offer-v2.5.0重写中,我们是如何做的
整体架构上采用MVP模式
- V和P通过接口实现交互,M只被P处理,P处理完后通过接口反馈给V
- V和P通过接口实现交互,M只被P处理,P处理完后通过接口反馈给V
代码可移植性和可扩展性
- 按照是否通用原则,命名包名——非通用包名,放在offer包下;通用功能模块,放在非offer包下。这样有利于快速移植,或者打成JAR包
- 接口实现,减少继承——在类的继承中,减少继承体系中每层父类的职责范围
HTTP请求
json解析
- Gson,这是综合考虑Json使用场景后的选择结果——方便json字符串到对象之间的转换、Gson在处理短的json时速度比较快、Gson不需要太多的注解
图片加载
采用Android-Universal-Image-Loader
- 多线程图片加载
- 可自定义的加载器
- 可自定义的图片显示回调
- 图片三级缓存——内存和硬盘缓存的图片通过多种数据结构管理
- 监听加载过程
bitmap显示质量设置为RGB_565——一个像素需要16位表示,Android中默认为ARGB_8888——一个像素需要32位表示。这种方式在基本保障图片质量要求的同时,还能够大大减少手机的内存占用
数据存储
- 由于目前客户端还未涉及到大量数据和离线加载模式,所以我们暂不使用数据库(以后在做IM或者离线加载时,会考虑使用ORM来访问数据库),综合速度考虑使用Android的SharedPreference
组件通信库EventBus - 吴强
EventBus是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间、组件与后台线程间的通信。
三个主要元素:
- Event:事件
Event可以是任意类型的对象
- Subscriber:事件订阅者,接收特定的事件
在EventBus中,使用约定来指定事件订阅者以简化使用。即所有事件订阅都都是以onEvent开头的函数,具体来说,函数的名字是onEvent,onEventMainThread,onEventBackgroundThread,onEventAsync这四个
- Publisher:事件发布者,用于通知Subscriber有事件发生
可以在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法,可以自己实例化EventBus对象,但一般使用默认的单例就好了:EventBus.getDefault(),根据post函数参数的类型,会自动调用订阅相应类型事件的函数。
ThreadMode
Subscriber函数的名字只能是那4个,因为每个事件订阅函数都是和一个ThreadMode相关联的,ThreadMode指定了会调用的函数。有以下四个ThreadMode:
- PostThread:
事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程。对应的函数名是onEvent。 - MainThread:
事件的处理会在UI线程中执行。事件处理时间不能太长,这个不用说的,长了会ANR的,对应的函数名是onEventMainThread。 - BackgroundThread:
事件的处理会在一个后台线程中执行,对应的函数名是onEventBackgroundThread,虽然名字是BackgroundThread,事件处理是在后台线程,但事件处理时间还是不应该太长,因为如果发送事件的线程是后台线程,会直接执行事件,如果当前线程是UI线程,事件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。 - Async:
事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作,每个事件会开启一个线程(有线程池),但最好限制线程的数目。
注册事件与解除注册
- 通过EventBus.getDefault().register方法可以向EventBus注册来订阅事件
- 通过registerSticky可以注册Stick事件处理函数
- 通过EventBus.getDefault().unregister方法解除EventBus事件订阅
Post事件
- 直接调用EventBus.getDefault().post(Event)就可以发送事件,根据Event的类型就可以发送到相应事件的订阅者。
- 当通过postSticky发送一个事件时,这个类型的事件的最后一次事件会被缓存起来,当有订阅者通过registerSticky注册时,会把之前缓存起来的这个事件直接发送给它
缺点
无法进程间通信,如果一个应用内有多个进程的话就没办法了
注意事项及要点
- 同一个onEvent函数不能被注册两次,所以不能在一个类中注册同时还在父类中注册
- 当Post一个事件时,这个事件类的父类的事件也会被Post。
- Post的事件无Subscriber处理时会Post
NoSubscriberEvent
事件,当调用Subscriber失败时会PostSubscriberExceptionEvent
事件。
其他
EventBus中还有个Util包,主要作用是可以通过AsyncExecutor执行一个Runnable,通过内部的RunnableEx(可以搜索异常的Runnable)当Runnable抛出异常时通过EventBus发消息显示错误对话框。
参考资料:快速Android开发系列通信篇之EventBus
如何提高代码质量——吴明
- code review
- 命名规范
- java命名规范:
- java类:m+模块+功能(如:mLoginRegester)
- 控件类:m+模块+功能+控件缩写(如:mLoginRegesterBtn)
- 常量:模块+功能(如:SCHOOL_COUNT_MAX)
- xml命名规范
- id:模块+功能+控件缩写
- color,string,dimen,drawable
- 图片:模块功能备注_状态
- 注释
- java命名规范:
- 好的框架
- okhttp
- Gson
- 设计模式:
- 处理异常
优化性能
代码测试工具:静态代码分析
如何写一份好的代码 - 张超耀
数据结构和核心算法
数据结构的重要性:低水平程序员总在考虑代码,高水平程序员总在考虑数据结构及其之间的关系
数据结构决定算法,数据结构考虑清楚了,核心的算法自然就出来了,这就是关于每个类的每个方法如何实现的问题
功能实现
思路确定后,实现过程也需要大量的构思活动。碰到比较熟悉有经验的领域,自然可以轻车熟路,但难免会有一些你不太熟悉的技术需要尝试。作为一个程序员,最大的挑战也是最大的乐趣所在,就是不断学习新的技术,没有这样的心态,很快就会落后。
那么遇到不熟悉的技术怎么办?Demo先行,这样做的好处是把单个技术问题和其他潜在的bug隔离开来,便于快速学习新技术。否则,直接在项目里写代码出错以后,要判断问题的源头都要多费好几倍的精力。
测试
- 测试很重要,设计测试用例就像开发时设计数据结构一样,也是很关键的。
代码可读性
要想自己满意,代码的可读性一定要好。要做到一年后甚至几年后你拿到自己写的代码,还能很容易看明白当时的思路和实现。这就涉及到命名和注释的问题
命名就像超市里的商品标签一样,要让看得人一目了然就知道这是个什么东西
注释也是很重要的,它可以用来说明一段代码的作用,算法的设计思想,或者是方法调用的参数格式要求等
最后总结一下:
- 技术水平是可以慢慢提高的,但是好的编程习惯需要从一开始就养成,它会让你在前进的道路上事半功倍,受益终生。
怎样写好的代码 - 曾铭
有两种方式构建软件设计:一种是把软件做得很简单以至于明显找不到缺陷;另一种是把它做得很复杂以至于找不到明显的缺陷
——C.A.R. Hoare
谁来写?角色的定义
- 程序员 vs 工程师
- 实现功能 vs 解决问题
- 搭个帐篷 vs 照顾孩子
好的代码?
一个程序员更希望接手一个有bug但是看的懂的工程,还是一个没bug但是看不懂的工程?
- 代码跟人聊天,解释做什么,注释解释为什么这么做,要注意什么
- 面向接口而不仅仅面向对象
举例:在 APP 开发和 API 开发之间,面向 API 文档做开发
写的过程?(推荐开发过程)
- 需求明确(理解来源及演进)
- 整体设计(外在联系,临界条件,错误处理)
- 实现(验证思路,解决问题及优化)切记不要拿到需求直接跳到这一步
好?目标
- 可运行,可读,可维护,可测试
- 参见 TDD,BDD